Expose response headers in DocsService - #6191
Conversation
DocsService
|
Would you mind share the screenshot or screencast that demonstrates the change for easier reviews? |
Sure! I've just added a screenshot to make it easier to understand. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #6191 +/- ##
============================================
+ Coverage 74.46% 74.60% +0.14%
- Complexity 22234 22470 +236
============================================
Files 1963 1972 +9
Lines 82437 82992 +555
Branches 10764 10798 +34
============================================
+ Hits 61385 61915 +530
- Misses 15918 15931 +13
- Partials 5134 5146 +12 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| endpointPath?: string, | ||
| queries?: string, | ||
| ): Promise<string> { | ||
| ): Promise<{ body: string; headers: Record<string, string> }> { |
There was a problem hiding this comment.
We should return ResponseData to preserve the header ordering and support multi-value headers. By doing so, we can remove extractHeaders below as well.
There was a problem hiding this comment.
Thanks for your review ! Belows are the changes :
- Introduced
ResponseDatato handle response (body + headers). - Implemented multi-value headers handling using
Map<string, string[]>. - Updated header display logic in DebugPage to follow RFC9110 §5.2 guidelines.
RFC9110 specifies:
"A sender MUST NOT generate multiple header fields with the same field name unless the entire field value is comma-separated or the field explicitly allows multiple field lines."
Thus, I defined ResponseData in types.tsx as:
Replaced Response with <ResponseData> in abstract doSend<Response>, and aggregated response headers:
Adjusted DebugPage header display:
When server set headers like this :
final HttpHeaders headers = HttpHeaders.builder()
.add("x-role", "admin")
.add("x-role", "editor")
.add("x-role", "user")
.build();
Now, multi-value headers appear correctly as below:
| Response Headers |
|---|
![]() |
If you have any feedback or suggestions, I’d really appreciate your comments!
There was a problem hiding this comment.
Thanks! It looks much better. However, I'd like you to consider the following:
- The RFC you mentioned doesn't prohibit specifying multiple header values with the same header name if the multiple field lines are allowed explicitly.
- At the protocol level, these two are different:
vs.
x-role: admin, editor, userx-role: admin x-role: editor x-role: user
Therefore, what do you think about using plaintext rather than JSON to render the response headers?
There was a problem hiding this comment.
Please also note that the following are considered different, although they are semantically same:
x-role: admin
other-header: other value
x-role: user
vs.
x-role: admin
x-role: user
other-header: other value
.. which means we need to preserve the ordering, which cannot be achieved by using a Map.
There was a problem hiding this comment.
Initially, I didn’t consider the preservation of header order or merging rules.
However, after reviewing RFC 9110, I found that only headers defined using the #element syntax are safe to merge using commas.
For other headers, merging can result in loss of semantics or parsing errors.
For example, the Date header value like "Sat, 19 Apr 2025 09:00:00 GMT" includes a comma as part of the value.
Blindly splitting on commas would incorrectly produce lines like:
date: Sat
date: 19 Apr 2025 09:00:00 GMT
Initially, I implemented naive comma-splitting across all headers.
After identifying this issue, I refined the logic to selectively split only list-type headers, while preserving others unchanged.
The updated logic iterates through the response Headers object:
-
If a header is recognized as a list-type (e.g., Accept, Cache-Control), it splits the value by comma and renders each entry individually.
-
Otherwise, it preserves the header as a single line to avoid unintended splitting.
| Example |
|---|
![]() |
Multi-value headers will be presented like this : X-Header: v1, v2, v3
Merge additional modification
It affects to modal. and In modal, we can't change method. so it can be removed.
headersObj -> header
Update PR comment
Transport layer don't parse header
Refer to type.tsx const listHeader, Separate lines if header couldn't be separated
Update review
| setResponseCache((prev) => ({ | ||
| ...prev, | ||
| [currentApiId]: { | ||
| body, | ||
| headers: responseHeaders, |
There was a problem hiding this comment.
It would be more useful if we could record the execution time and display it in the debug console. Some may want to know whether the response is outdated or up-to-date.
There was a problem hiding this comment.
I think showing the execution time on the debug page could improve the user experience!
If we decide to display it, where would be the best place to show the execution time?
I was thinking of showing it in a separate section, similar to other API testing tools.
There was a problem hiding this comment.
It sounds good to me. Could you prototype your idea? I’d like to see how it looks and feels.
There was a problem hiding this comment.
In modern API Testing tools, they display Response status, execution time, and ResponseData size.
So i was thinking what if we displayed those three information on the right section of response page Util.
Examples :
| Postman | Bruno |
|---|---|
![]() |
![]() |
Prototype :
| ResponsePage | DebugConsole |
|---|---|
![]() |
![]() |
More specifically, I thought of two possible versions :
| Text-like | Colored |
|---|---|
![]() |
![]() |
--- Updated ---
I put together a quick prototype implementation. Let me know what you think !
| Fail | Success |
|---|---|
![]() |
![]() |
Executed time should be rounded tho :)
There was a problem hiding this comment.
The prototype looks great! Since the results are cached, would you mind also adding the timestamp of when the request was executed?
There was a problem hiding this comment.
Sure! It’s necessary when we’re caching ResponseData.
I’ve just added the update :)
- As previously discussed, the cache now stores the entire ResponseData object instead of selected values.
- Updated the DebugPage to display the newly added fields from ResponseData. The logic for determining colors is as follows: Diff
Here are some examples pictures:
| 2xx | 4xx | Invalid |
|---|---|---|
![]() |
![]() |
![]() |
Co-authored-by: jrhee17 <guins_j@guins.org>
… into add-Response-Header
Motivation: JSON pretty-printing logic was unintentionally removed while working on line#6191 https://github.com/line/armeria/pull/6191/files#diff-f12e66c572a486106958b3f165d995f093e16ffb76db3028fd836595eace6c67L64-L67 Modifications: - Prettify JSON responses before rendering them on the debug console. - Remove duplicate duration measurement logic. - Slightly adjust the font size and layout spacing in `ResponseStatusBar` for readability. Result: Fix a regression where JSON responses are not formatted in DocService













Motivation
This PR addresses issue : #6187
“A response body is exposed in DocService but response headers are not. Since some APIs convey important information through response headers, exposing them in the debug console would be useful.”
Modifications
Modified the transport to return response headers along with the body when a request is made from the DocService’s debug console.
Updated the UI to display response headers in addition to the response body.
Changed the tooltip of the “Copy” button to say “Copy response body” for better clarity.
Improved API switching behavior in the debug UI
Screenshots
Before : When we click on another API in Docs, the existing response remains.
After : When we click on another API, the response value of the previous API remains on the previous page.
Result